/******************************************************
* Team WB
* Brett Cate, William Kim
* 
* Main Program File
* This program handles the user interface for the SC-2
* computer simulation. 
*******************************************************/

#include "main.h"

/*
Main function to run the simulator.
*/
int main() {
	int input = 0;
	int cpuError, debugError;

	CPUPtr my_cpu = cpu_new(&cpuError);
	RegisterPtr swPtr = my_cpu->SW;
	RegisterFilePtr my_registers = my_cpu->registerFile;
	MemoryModulePtr my_memory = my_cpu->mainMemory;
	ALUPtr my_alu = my_cpu->alu;

	DebugMonitorPtr my_debugger = debugMonitor_new(my_cpu, &debugError);

	register_putRegValue(swPtr, 0x0000);
	 
	printf("************************************\n");
	printf("*                                  *\n");
	printf("* Welcome to the WB SC-2 Simulator *\n");
	printf("* Programmers: Brett Cate          *\n");
	printf("*              William Kim         *\n");
	printf("*                                  *\n");
	printf("************************************\n\n");

	while (input != 5) {
		input = main_mainMenu();
		switch (input) {
			case 1 :
				debugMonitor_loadProgram(my_debugger,"program.txt");
				debugMonitor_runProgram(my_debugger);
				cpu_run(my_cpu,(struct DebugMonitor*)my_debugger);
				break;
			case 2 :
				main_registerMenu(my_registers);
				break;
			case 3 :
				main_memoryMenu(my_memory);
				break;
			case 4 :
				main_aluMenu(my_alu);
				break;
			default :
				break;
		}
	}
}

/* 
Displays main menu options and prompts for user choice.

return value: the user choice.
*/
int main_mainMenu() {
	
	int mainMenu_input = 0;
	while (mainMenu_input < 1 || mainMenu_input > 5) {
		printf("Please choose an option: \n");
		printf("1. Run program\n");
		printf("2. Registers\n");
		printf("3. Memory\n");
		printf("4. ALU\n");
		printf("5. Quit\n");
			
		scanf("%d", &mainMenu_input);
		printf("\n");
	}
	return mainMenu_input;

}

/* 
Displays register menu options and prompts for user choice.
*/
void main_registerMenu(RegisterFilePtr the_registerFilePtr) {
	int registerMenu_input = 0;
	int registerMenu_index = 0;
	int which_byte = 0;
	int registerMenu_error = NO_ERROR;
	int registerNumber, registerValue;
	while (registerMenu_input != 6) {
		registerMenu_error = NO_ERROR;
		printf("Please choose an option: \n");
		printf("1. Display Registers\n");
		printf("2. Get Byte From...\n");
		printf("3. Put Byte To...\n");
		printf("4. Get Register Value...\n");
		printf("5. Put Register Value...\n");
		printf("6. Back\n");		
			
		scanf("%d", &registerMenu_input);
		printf("\n");

		switch (registerMenu_input) {
			
			case 1 :
				printf("Register File\n");
				for (registerMenu_index = 0; registerMenu_index < 16; registerMenu_index++) {
					printf("$R%-3X :  %04X\n", registerMenu_index, *the_registerFilePtr[registerMenu_index]);
				}
				printf("\n");
				break;
			case 2 :
				printf("Enter a register number and which byte you want (1 : HOB; 0 : LOB): \n");
				scanf("%X %d", &registerNumber, &which_byte);
				printf("\n");
				printf("Value : %02X\n\n", registerFile_getByteFrom(the_registerFilePtr, registerNumber, which_byte, &registerMenu_error)); 
				break;
			case 3 :
				printf("Enter a register number, which byte (1 : HOB; 0 : LOB), and value : ");
				scanf("%X %d %X", &registerNumber, &which_byte, &registerValue);
				printf("\n");
				registerMenu_error = registerFile_putByteTo(the_registerFilePtr, registerNumber, which_byte, registerValue); 
				break;
			case 4 :
				printf("Enter a register number : ");
				scanf("%X", &registerNumber);
				printf("\n");
				printf("Value : %04X\n\n", registerFile_getRegValue(the_registerFilePtr, registerNumber, &registerMenu_error)); 
				break;
			case 5 :
				printf("Enter a register number and a value to put : ");
				scanf("%X %X", &registerNumber, &registerValue);
				printf("\n");
				registerMenu_error = registerFile_putRegValue(the_registerFilePtr, registerNumber, registerValue); 
				break;
			default :
				break;
		}
		if(registerMenu_error)
			printf("Register Error! : %s \n\n", main_errorMessage(registerMenu_error)); 
	}
}

/* 
Displays memory menu options and prompts for user choice.
*/
void main_memoryMenu(MemoryModulePtr the_memoryModulePtr) {
	int memoryMenu_input = 0;
	int memoryMenu_index = 0;
	int memoryMenu_error = NO_ERROR;
	int memAddr, memAddr2, memVal;
	
	while (memoryMenu_input != 6) {
		memoryMenu_error = NO_ERROR;
		printf("Please choose an option: \n");
		printf("1. Display Memory in Range...\n");
		printf("2. Fetch Byte From...\n");
		printf("3. Store Byte To...\n");
		printf("4. Fetch Word From...\n");
		printf("5. Store Word To...\n");
		printf("6. Back\n");		
			
		scanf("%d", &memoryMenu_input);
		printf("\n");

		switch (memoryMenu_input) {
			
			case 1 :
				printf("Enter a beginning and ending memory address : \n");
				scanf("%X %X", &memAddr, &memAddr2);
				for (memoryMenu_index = memAddr; memoryMenu_index <= memAddr2; memoryMenu_index++) {
					printf("%04X :  %02X\n", memoryMenu_index, memory_fetchByteFrom(the_memoryModulePtr,memoryMenu_index, &memoryMenu_error));
				}
				printf("\n");
				break;
			case 2 :
				printf("Enter a memory address : \n");
				scanf("%X", &memAddr);
				printf("\n");
				printf("Value : %02X\n\n", memory_fetchByteFrom(the_memoryModulePtr, memAddr, &memoryMenu_error)); 
				break;
			case 3 :
				printf("Enter a memory address and value to store : ");
				scanf("%X %X", &memAddr, &memVal);
				printf("\n");
				memoryMenu_error = memory_storeByteTo(the_memoryModulePtr, memAddr, memVal); 
				break;
			case 4 :
				printf("Enter a memory address : \n");
				scanf("%X", &memAddr);
				printf("\n");
				printf("Value : %04X\n\n", memory_fetchWordFrom(the_memoryModulePtr, memAddr, &memoryMenu_error)); 
				break;
			case 5 :
				printf("Enter a memory address and a value to store : ");
				scanf("%X %X", &memAddr, &memVal);
				printf("\n");
				memoryMenu_error = memory_storeWordTo(the_memoryModulePtr, memAddr, memVal); 
				break;
			default :
				break;
		}
		if(memoryMenu_error)
			printf("Memory Error! : %s \n\n", main_errorMessage(memoryMenu_error)); 
	}
}

/* 
Displays ALU menu options and prompts for user choice.
*/
void main_aluMenu(ALUPtr the_aluPtr) {
	int aluMenu_input = 0;
	int aluMenu_error = NO_ERROR;
	int aluFlags, aluRegNumber, aluRegVal, aluOp;
	
	while (aluMenu_input != 7) {
		aluMenu_error = NO_ERROR;
		printf("Please choose an option: \n");
		printf("1. Display ALU Registers\n");
		printf("2. Set Register...\n");
		printf("3. Perform Operation...\n");
		printf("4. Clear Flags\n");
		printf("5. Set Flags...\n");
		printf("6. Get Result...\n");
		printf("7. Back\n");		
			
		scanf("%d", &aluMenu_input);
		printf("\n");

		switch (aluMenu_input) {
			
			case 1 :
				printf("ALU Registers\n");
				printf("A : %04X     B  : %04X\n", *the_aluPtr->reg_A, *the_aluPtr->reg_B);
				printf("R : %04X     R2 : %04X\n", *the_aluPtr->reg_R, *the_aluPtr->reg_R2);
				printf("SW (external) : %04X\n", register_getRegValue(the_aluPtr->status_word_ptr,&aluMenu_error));
				printf("\n");
				break;
			case 4 :
				aluMenu_error = alu_clearFlags(the_aluPtr);
				printf("Flags cleared.\n");
				printf("\n");
				break;
			case 5 :
				printf("Enter a single hexadecimal digit representing the four-bits of flags : ");
				scanf("%X", &aluFlags);
				printf("\n");
				aluMenu_error = alu_setFlags(the_aluPtr, aluFlags);
				break;
			case 2 :
				printf("Indicate which register (0=A, 1=B, 2=R, 3=R2) and the value to set : \n");
				scanf("%X %X", &aluRegNumber, &aluRegVal);
				printf("\n");
				aluMenu_error = alu_setRegister(the_aluPtr, aluRegNumber, aluRegVal); 
				break;
			case 6 :
				printf("Indicate which result register (0=R, 1=R2) : \n");
				scanf("%X", &aluRegNumber);
				printf("\n");
				printf("R%1d = %04X\n",aluRegNumber,alu_getResult(the_aluPtr, aluRegNumber, &aluMenu_error)); 
				break;
			case 3 :
				printf("Indicate which operation you would like perform : \n");
				printf("  1 : ADD\n  2 : SUBTRACT\n  3 : MULTIPLY\n  4 : DIVIDE\n  5 : AND\n");
				printf("  6 : OR\n  7 : XOR\n  8 : NOT\n  9 : SHIFT LEFT\n  10: SHIFT RIGHT\n");
				scanf("%d", &aluOp);
				printf("\n");
				if (aluOp == 9)
					aluMenu_error = alu_SHL(the_aluPtr);
				else if (aluOp == 10)
					aluMenu_error = alu_SHR(the_aluPtr);
				else
					aluMenu_error = alu_op(the_aluPtr, aluOp-1);
				break;
			default :
				break;
		}

		if(aluMenu_error)
				printf("ALU Error! : %s \n\n", main_errorMessage(aluMenu_error)); 
	}
}

/*
Returns the respective error message given an error code.

parameter (the_error_code): the error code.

return value: an error message as a string.
*/
char* main_errorMessage(int the_error_code) {
	switch (the_error_code) {
		case NULL_POINTER_ERROR :
			return "Null pointer error.";
		case ADDRESS_OOB_ERROR :
			return "Address out of bounds of memory.";
		case INVALID_REG_NUM :
			return "Invalid register number.";
		case INVALID_BYTE_NUM :
			return "Invalid byte number.";
		case OUT_OF_MEM_ERROR :
			return "Error obtaining memory from the heap.";
		default :
			return "Unknown error code.";
	}
}

/* End Program */
